from flask import Flask, request, send_from_directory, render_template_string, redirect, url_for, abort
import os

app = Flask(__name__)
UPLOAD_FOLDER = '.'  # 上传目录
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# 安全路径检查，防止目录遍历攻击
def safe_join(base, *paths):
    full_path = os.path.join(base, *paths)
    if not os.path.abspath(full_path).startswith(os.path.abspath(base)):
        abort(403, description="禁止访问该路径")
    return full_path

@app.route('/', defaults={'path': ''}, methods=['GET', 'POST'])
@app.route('/<path:path>', methods=['GET', 'POST'])
def serve(path):
    current_path = safe_join(app.config['UPLOAD_FOLDER'], path)

    # 处理文件上传
    if request.method == 'POST':
        if not os.path.isdir(current_path):
            abort(400, "只允许上传到目录。")

        file = request.files.get('file')
        if not file or not file.filename:
            return "请选择文件", 400
        
        file.save(safe_join(current_path, file.filename))
        return redirect(url_for('serve', path=path))

    # 如果是目录，则列出内容
    if os.path.isdir(current_path):
        try:
            dir_items = os.listdir(current_path)
        except PermissionError:
            abort(403, description="权限不足")

        parent_path = os.path.dirname(path) if path else None

        items = []
        for name in dir_items:
            item_is_dir = os.path.isdir(os.path.join(current_path, name))
            items.append({
                'name': name + ('/' if item_is_dir else ''),
                'path': os.path.join(path, name)
            })

        return render_template_string('''
            <h1>文件管理器 - 当前目录: /{{ path }}</h1>
            
            {% if path %}
            <p><a href="{{ url_for('serve', path=parent_path) }}">⬆ 返回上级目录</a></p>
            {% endif %}
            
            <h2>项目列表</h2>
            <ul>
                {% for item in items %}
                    <li><a href="{{ url_for('serve', path=item.path) }}">{{ item.name }}</a></li>
                {% endfor %}
            </ul>
            
            <hr>
            <h2>上传文件到 /{{ path }}</h2>
            <form method="post" enctype="multipart/form-data">
                <input type="file" name="file">
                <input type="submit" value="上传">
            </form>
        ''', path=path, items=items, parent_path=parent_path)

    # 如果是文件，则提供下载
    elif os.path.isfile(current_path):
        directory, filename = os.path.split(path)
        base_dir = safe_join(app.config['UPLOAD_FOLDER'], directory)
        return send_from_directory(base_dir, filename, as_attachment=True)

    # 如果路径不存在
    else:
        abort(404, description="文件或目录不存在")

# 错误处理
@app.errorhandler(404)
@app.errorhandler(403)
@app.errorhandler(400)
def handle_error(e):
    return f"<h1>错误 {e.code}</h1><p>{e.description}</p><a href='/'>返回主页</a>", e.code

if __name__ == '__main__':
    print(f"服务器启动成功！上传目录: {os.path.abspath(app.config['UPLOAD_FOLDER'])}")
    app.run(host='0.0.0.0', port=7000, debug=True)
